package model

import (
	"gitee.com/wzpwzy/gogii/conf"
	"gitee.com/wzpwzy/gogii/tools"
	"strings"
)

type Table struct {
	Name    string `gorm:"column:Name"`
	Comment string `gorm:"column:Comment"`
}

type Field struct {
	Field      string `gorm:"column:Field"`
	Type       string `gorm:"column:Type"`
	Null       string `gorm:"column:Null"`
	Key        string `gorm:"column:Key"`
	Default    string `gorm:"column:Default"`
	Extra      string `gorm:"column:Extra"`
	Privileges string `gorm:"column:Privileges"`
	Comment    string `gorm:"column:Comment"`
}

func Genertate(path string, isEs int, tableNames ...string) (modelName []Table) {
	tableNamesStr := ""
	for _, name := range tableNames {
		if tableNamesStr != "" {
			tableNamesStr += ","
		}
		tableNamesStr += "'" + name + "'"
	}
	tables := getTables(tableNamesStr) //生成所有表信息
	var model Table
	for _, table := range tables {
		fields := getFields(table.Name)
		model.Name = generateModel(table, fields, path, isEs)
		modelName = append(modelName, model)
	}
	return modelName
}

// 获取表信息
func getTables(tableNames string) []Table {
	db := tools.GetMysqlDb()
	var tables []Table
	if tableNames == "" {
		db.Raw("SELECT TABLE_NAME as Name,TABLE_COMMENT as Comment FROM information_schema.TABLES WHERE table_schema='" + conf.MasterDbConfig.DbName + "';").Find(&tables)
	} else {
		db.Raw("SELECT TABLE_NAME as Name,TABLE_COMMENT as Comment FROM information_schema.TABLES WHERE TABLE_NAME IN (" + tableNames + ") AND table_schema='" + conf.MasterDbConfig.DbName + "';").Find(&tables)
	}
	return tables
}

// 获取所有字段信息
func getFields(tableName string) []Field {
	db := tools.GetMysqlDb()
	var fields []Field
	db.Raw("show FULL COLUMNS from " + tableName + ";").Find(&fields)
	return fields
}

//生成Model
/**
*生成model
*字段类型 fieldName        fieldType 						fieldComment			fieldJson
*字段列子 UpdatedAt        uint   `orm:"column(updated_at)" description:"更新时间" json:"updated_at"`
 */
func generateModel(table Table, fields []Field, path string, isEs int) string {
	content := "package models\n\n"
	//表注释
	if len(table.Comment) > 0 {
		content += "// " + table.Comment + "\n"
	}
	if isEs > 0 {
		content += `import (
			"gitee.com/wzpwzy/gogii/client"
			"github.com/olivere/elastic/v7"
		)` + "\n"
	}
	if conf.MasterDbConfig.Prefix != "" {
		table.Name = strings.Replace(table.Name, conf.MasterDbConfig.Prefix, "", -1)
	}
	content += "type " + tools.Case2Camel(table.Name) + " struct {\n"
	//生成字段
	for _, field := range fields {
		fieldName := tools.Case2Camel(field.Field)
		fieldGorm := getFieldGorm(field)
		fieldJson := getFieldJson(field)
		fieldType := getFiledType(field)
		fieldComment := getFieldComment(field)

		content += "	" + fieldName + " " + fieldType + " `" + fieldJson + " " + fieldGorm + " " + fieldComment + " " + "binding:\"omitempty\"" + "`\n"
	}
	if isEs > 0 {
		content += "	" + "Size      int    `json:\"size\"`" + "`\n"
		content += "	" + "Page      int    `json:\"page\"`" + "`\n"
	}
	content += "}\n"
	//是否生成Es mapping
	if isEs > 0 {
		content += getMapping(fields)
		content += getFilter(fields, tools.Case2Camel(table.Name))
	}

	filename := ""
	if path == "" {
		filename = conf.ModelPath + table.Name + ".go"
	} else {
		filename = path + table.Name + ".go"
	}

	return tools.Write(filename, table.Name, content, conf.ModelPath)
}

func getMapping(fields []Field) string {
	content := "func GetMapping() map[string]interface{} {\n"
	content += "	" + "type mi = map[string]interface{}\n"
	content += `
    	mapping := mi{
		"settings": mi{
			"number_of_shards":   5,
			"number_of_replicas": 1,
			"refresh_interval":   "1s",
		},
		"mappings": mi{
			"properties": mi{ 
    `
	for _, field := range fields {
		fieldType := getFiledEsType(field)
		content += "				" + `"` + field.Field + fieldType + "\n"
	}
	content += `
   				},
			},
		} 
		return mapping
	}
   ` + "\n"
	return content
}

func getFilter(fields []Field, Name string) string {
	content := "func (r * " + Name + ") ToFilter() *client.EsSearch {\n"
	content += "	" + "var search client.EsSearch\n"
	for _, field := range fields {
		fieldName := tools.Case2Camel(field.Field)
		fieldType := getFiledType(field)
		if strings.Compare(fieldType, "string") == 0 {
			content += "	" + "if len(r." + fieldName + ") != 0 {\n"
		} else {
			content += "	" + "if " + fieldName + " != 0 {\n"
		}
		content += "	" + `search.ShouldQuery = append(search.ShouldQuery, elastic.NewMatchQuery("` + field.Field + `", r.` + fieldName + `))` + "\n"
		content += "	" + "}\n"
	}
	content += `if search.Sorters == nil {
			search.Sorters = append(search.Sorters, elastic.NewFieldSort("id").Desc())
		}
		search.From = (r.Page - 1) * r.Size
		search.Size = r.Size
		return &search
}` + "\n"
	return content
}

// 获取字段类型
func getFiledType(field Field) string {
	typeArr := strings.Split(field.Type, "(")
	IsUnit := strings.Contains(field.Type, "unsigned")
	FieldType := ""
	switch typeArr[0] {
	case "int":
		FieldType = "int"
	case "integer":
		FieldType = "int"
	case "mediumint":
		FieldType = "int"
	case "bit":
		FieldType = "int"
	case "year":
		FieldType = "int"
	case "smallint":
		FieldType = "int"
	case "tinyint":
		FieldType = "int"
	case "bigint":
		FieldType = "int64"
	case "decimal":
		FieldType = "float32"
	case "double":
		FieldType = "float32"
	case "float":
		FieldType = "float32"
	case "real":
		FieldType = "float32"
	case "numeric":
		FieldType = "float32"
	case "timestamp":
		FieldType = "time.Time"
	case "datetime":
		FieldType = "string"
	case "time":
		FieldType = "time.Time"
	default:
		FieldType = "string"
	}

	if FieldType == "int" && IsUnit == true {
		FieldType = "uint"
	}
	if FieldType == "int64" && IsUnit == true {
		FieldType = "uint64"
	}

	return FieldType
}

// 获取字段json描述
func getFieldJson(field Field) string {
	return `json:"` + field.Field + `"`
}

// 获取字段说明
func getFieldComment(field Field) string {
	if len(field.Comment) > 0 {
		return `description:"` + field.Comment + `"`
	}
	return ""
}

// 获取字段说明
func getFieldGorm(field Field) string {
	str := `gorm:"column:` + field.Field + `;`
	if field.Field == "id" {
		str += `primary_key;auto_increment;`
	}
	if field.Default == "" {
		str += `default:''` + `"`
	} else {
		str += `default:` + field.Default + `"`
	}

	return str
}

// 获取Es字段类型
func getFiledEsType(field Field) string {
	typeArr := strings.Split(field.Type, "(")
	FieldType := ""
	switch typeArr[0] {
	case "int":
		FieldType = `":mi{"type": "integer"},`
	case "integer":
		FieldType = `":mi{"type": "integer"},`
	case "mediumint":
		FieldType = `":mi{"type": "integer"},`
	case "bit":
		FieldType = `":mi{"type": "integer"},`
	case "year":
		FieldType = `":mi{"type": "integer"},`
	case "smallint":
		FieldType = `":mi{"type": "integer"},`
	case "tinyint":
		FieldType = `":mi{"type": "integer"},`
	case "bigint":
		FieldType = `":mi{"type": "long"},`
	case "decimal":
		FieldType = `":mi{"type": "float"},`
	case "double":
		FieldType = `":mi{"type": "float"},`
	case "float":
		FieldType = `":mi{"type": "float"},`
	case "real":
		FieldType = `":mi{"type": "float"},`
	case "numeric":
		FieldType = `":mi{"type": "float"},`
	case "timestamp":
		FieldType = `":mi{"type": "long"},`
	case "datetime":
		FieldType = `mi{"type": "date", "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"},`
	case "date":
		FieldType = `mi{"type": "date", "format": "yyyy-MM-dd||epoch_millis"},`
	case "time":
		FieldType = `":mi{"type": "long"},`
	default:
		FieldType = `":mi{"type": "text"},`
	}

	return FieldType
}
